<?php
namespace app\controllers;

use yii\db\Query;
use yii\helpers\VarDumper;
use yii\web\Controller;
use Yii;
use yii\web\UnauthorizedHttpException;

class ApiController extends Controller
{
    public $ami         = null;
    public $cdr_db      = null;
    public $asterisk_db = null;
    public $enableCsrfValidation = false;

    public function init()
    {
        $this->cdr_db = Yii::$app->get('cdr_db');
        $this->asterisk_db = Yii::$app->get('asterisk_db');
        $this->ami = new \AGI_AsteriskManager();
        if (YII_DEBUG == false){
            if ($this->ami->connect()==false){
                return $this->asJson(['code'=>1,'msg'=>'ami未连接']);
            }
        }

    }

    /**
     * 呼叫接口
     * @param null $exten
     * @param null $aPhone
     * @param null $xPhone
     * @param null $phone
     */
    public function actionDial()
    {
        $exten = Yii::$app->request->post('exten',null);
        $phone = Yii::$app->request->post('phone',null);
        // 指定允许其他域名访问
        header('Access-Control-Allow-Origin:*');
        // 响应类型
        header('Access-Control-Allow-Methods:POST');
        // 响应头设置
        header('Access-Control-Allow-Headers:x-requested-with,content-type');

        //验证请求参数
        if (empty($exten) || !is_numeric($exten) || empty($phone)){
            return $this->asJson(['code'=>1,'msg'=>'参数错误：外呼号或客户号码不正确']);
        }
        //验证分机状态
        $extenStatus = $this->ami->ExtensionState($exten,'from-internal');
        if ($extenStatus['Status'] != 0) {
            return $this->asJson(['code' => 1, 'msg' => '分机未空闲无法拨打']);
        }
        $variable = null;
        //最后一个参数为true表示异步拨打
        $data = $this->ami->Originate("SIP/".$exten,$phone,'from-internal',1,null,null,null,$exten,$variable,null,true);
        $this->asJson(['code'=>0,'msg'=>'已发送呼叫请求','data'=>$data]);

    }

    /**
     * 双向回拨
     * @param null $fromPhone
     * @param null $toPhone
     * @param null $login_name
     * @param null $uid
     */
    public function actionDialBack()
    {
        $fromPhone = Yii::$app->request->post('fromPhone',null);
        $toPhone = Yii::$app->request->post('toPhone',null);
        //验证手机号码
        if (!$this->verifyPhone($fromPhone) || !$this->verifyPhone($toPhone)) {
            return $this->asJson(['code' => 1, 'msg' => '手机号格式不正确!']);
        }
        //设置通道变量
        //$variable = "CDR(login_name_uid)=".$login_name."_".$uid;
        try {
            //查询可使用的中继channelid
            $trunk = (new Query())->from('trunks')->innerJoin('outbound_route_trunks','trunks.trunkid=outbound_route_trunks.trunk_id')
                ->select(['trunks.channelid'])
                ->orderBy('outbound_route_trunks.seq asc')
                ->one($this->asterisk_db);
            if ($trunk==false){
                return $this->asJson(['code'=>1,'msg'=>'无可用线路,请配置中继和出局路由']);
            }
            $trunk_channelid = $trunk['channelid'];

            //检测是否有拨号计划$exten，没有则创建
            $exten = 'dial-back';
            $extensionPath = Yii::$app->params['asterisk']['etc'] . Yii::$app->params['asterisk']['extensions_custom'];
            $fileContent = file_get_contents($extensionPath);
            if (strpos($fileContent, "[".$exten."]") === false) {
                $extStr = [
                    '['.$exten.']',
                    'exten => _x.,1,Answer',
                    'exten => _x.,n,Set(CDR(calldate)=${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)})',
                    ';exten => _x.,n,Background(custom/calling)',
                    'exten => _x.,n,Set(NOW=${EPOCH})',
                    'exten => _x.,n,Set(__DAY=${STRFTIME(${NOW},,%d)})',
                    'exten => _x.,n,Set(__MONTH=${STRFTIME(${NOW},,%m)})',
                    'exten => _x.,n,Set(__YEAR=${STRFTIME(${NOW},,%Y)})',
                    'exten => _x.,n,Set(__TIMESTR=${YEAR}${MONTH}${DAY}-${STRFTIME(${NOW},,%H%M%S)})',
                    'exten => _x.,n,Set(__CALLFILENAME=${CALLERID}-${EXTEN}-${TIMESTR}-${UNIQUEID})',
                    'exten => _x.,n,Set(MIXMONFILE=' . Yii::$app->params['asterisk']['monitor'] . '${YEAR}/${MONTH}/${DAY}/${CALLFILENAME}.${MIXMON_FORMAT})',
                    'exten => _x.,n,MixMonitor(${MIXMONFILE},b,${MIXMON_POST})',
                    'exten => _x.,n,Set(CDR(recordingfile)=${MIXMONFILE})',
                    'exten => _x.,n,Dial(SIP/' .$trunk_channelid.'/'. '${EXTEN})',
                    'exten => h,1,AGI(dgg/cdr.php)',
                    'exten => _x.,n,Hangup',
                ];
                $file = fopen($extensionPath, "a");
                fwrite($file,"\n");
                foreach ($extStr as $line) {
                    fwrite($file, $line . "\n");
                }
                fwrite($file,"\n");
                fclose($file);
                $this->reload();
            }

            //写入自动拨号文件
            $tempArr = [
                "Channel:SIP/" . $trunk_channelid .'/'. $fromPhone,
                "CallerID:" . $trunk_channelid,
                "MaxRetries:0",
                "RetryTime:0",
                "Context:".$exten,
                "Extension:" . $toPhone,
                "Archive:Yes",
                //"Setvar:".$variable,
            ];
            $fileName = Yii::$app->params['asterisk']['outgoing'] . $fromPhone . "-" . $toPhone . "-" . time() . uniqid() . mt_rand(1000, 9999) . ".call";
            $file = fopen($fileName, "w");
            if ($file) {
                foreach ($tempArr as $line) {
                    fwrite($file, $line . "\n");
                }
            }
            fclose($file);
            return $this->asJson(['code' => 0, 'msg' => '请求成功']);
        } catch (\Exception $e) {
            return $this->asJson(['code' => 1, 'msg' => '请求失败','data'=>$e->getMessage()]);
        }
    }

    /**
     * 通过分机号挂断当前通话
     * @param null $exten
     */
    public function actionHangup()
    {
        $exten = Yii::$app->request->post('exten',null);
        header('Access-Control-Allow-Origin:*');
        // 响应类型
        header('Access-Control-Allow-Methods:POST');
        // 响应头设置
        header('Access-Control-Allow-Headers:x-requested-with,content-type');

        $channel = $this->getChannel($exten);
        if ($channel == false) {
            return $this->asJson(['code' => 1, 'msg' => '未在通话中']);
        }
        $res = $this->ami->Hangup(trim($channel));
        if ($res['Response'] == 'Success') {
            return $this->asJson(['code' => 0, 'msg' => '已挂断', 'data' => $res]);
        }
        return $this->asJson(['code' => 1, 'msg' => '未挂断', 'data' => $res]);
    }

    /**
     * 分机监听分机
     * @param null $exten 监听分机
     * @param null $spyExten 被监听分机
     * @param null $type 监听类型 default=3
     * 1-密语，客户听不到监听者说话 （常用）
     * 2-强插，三方正常通话
     * 3-监听，只能听
     */
    public function chanSpy()
    {
        $exten = Yii::$app->request->post('exten',null);
        $spyExten = Yii::$app->request->post('spyExten',null);
        $type = Yii::$app->request->post('type',null);
        //验证监听分机状态,监听分机必需空闲
        $extenStatus = $this->ami->ExtensionState($exten,'from-internal');
        if ($extenStatus['Status'] != 0) {
            $this->asJson(['code' => 1, 'msg' => '分机：' . $exten . '不在空闲状态']);
        }
        //验证被监听分机状态,被监听分机必需使用中
        $spyExtenStatus = $this->ami->ExtensionState($spyExten,'from-internal');
        if ($spyExtenStatus['Status'] != 1) {
            $this->asJson(['code' => 1, 'msg' => '分机：' . $spyExten . '不在通话中']);
        }
        if (!in_array($type,[1,2,3])){
            $this->asJson(['code'=>1,'msg'=>'请传入监听模式']);
        }
        switch ($type) {
            case 1:
                $dataExt = "Ew";
                break;
            case 2:
                $dataExt = "EB";
                break;
            case 3:
                $dataExt = "E";
                break;
            default:
                $dataExt = "";
                break;
        }
        $data = "SIP/" . $spyExten . "," . $dataExt;
        $rs = $this->ami->Originate("SIP/" . $exten, $spyExten, 'from-internal', 1, "ChanSpy", $data, null, $exten, null, null, true);
        if ($rs['Response'] != 'Error') {
            return $this->asJson(['code' => 0, 'msg' => '监听连接成功', 'data' => $rs]);
        }
        return $this->asJson(['code' => 1, 'msg' => '系统错误', 'data' => $rs]);
    }

    /**
     * 播放通话录音、下载录音
     * @param null $action
     * @param null $name
     */
    public function actionRecord()
    {
        $action = Yii::$app->request->get('action',null);
        $name = Yii::$app->request->get('name',null);
        $file = decryptPhone('aes', $name);
        $path = Yii::$app->params['asterisk']['monitor'] . $file;
        if (!file_exists($path)) {
            exit('录音文件不存在');
        }
        if ($action == 'down') {
            $filenamedis = date('Y-m-d-H-i-s') . strrchr($file, '.');
            header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
            header("Content-Disposition: attachment; filename = " . $filenamedis);
            header("Content-Length: " . filesize($path));
            header("Content-Type: application/octet-stream");
            echo file_get_contents($path);
        } elseif ($action == 'play') {
            $fileSize = filesize($path);
            $etag = md5(filemtime($path));
            $fp = fopen($path, 'rb');
            if (!$fp) {
                die('Could not open file');
            }
            $start = 0;
            $end = $fileSize - 1;
            if (isset($_SERVER['HTTP_RANGE']) && !empty($_SERVER['HTTP_RANGE'])) {
                //获取请求头中的Range字段
                $range = explode('-', substr($_SERVER['HTTP_RANGE'], strlen('bytes=')));

                $start = $range[0];
                if ($range[1] > 0) {
                    $end = $range[1];
                }

                //构造断点续传响应头
                header('HTTP/1.1 206 Partial Content');
                header('Status: 206');
                header('Accept-Ranges: bytes');
                header('Content-Range: bytes ' . $start . '-' . $end . '/' . $fileSize);
                header('Content-Length: ' . ($end - $start + 1));
            } else {
                header('Content-Length: ' . $fileSize);
            }
            header('Content-Type: audio/mpeg');
            header('Last-Modified: ' . date('D, d M Y H:i:s \G\M\T', filemtime($path)));
            header('ETag: "' . $etag . '"');
            header('Expires: 0');
            if ($start > 0) {
                fseek($fp, $start); //移动文件指针
            }
            $bytesPosition = $start;
            while (!feof($fp) && $bytesPosition <= $end) {
                $chunk = 1024 * 1024 * 50; //每次读取50k
                if ($bytesPosition + $chunk > $end + 1) {
                    $chunk = $end - $bytesPosition + 1;
                }
                $chunk = fread($fp, $chunk);
                if (!$chunk) {
                    die('Could not read file');
                }
                print($chunk);
                flush();
                $bytesPosition += $chunk;
            }
            fclose($fp);
        }
    }

    /**
     * 获取通道By分机
     * @param string $exten 分机号码
     * @return boolean or channel
     */
    public function getChannel()
    {
        $exten = Yii::$app->request->get('exten',null);
        $rs = $this->ami->Command("core show channels");
        if ($exten) {
            $reg = '/SIP\/' . $exten . '-\S+\s?/';
        } else {
            $reg = '/SIP\/' . $this->exten . '-\S+\s?/';
        }
        preg_match($reg, $rs['data'], $channel);
        if (empty($channel)) {
            return false;
        } else {
            return $channel[0];
        }
    }

    /**]
     * 验证手机号码格式
     */
    public function verifyPhone($phone=null)
    {
        //不验证号码
        return true;
        $isPhone = "/^1[3456789][0-9]{9}$/";
        $isTel = "/^([0-9]{3,4})?[0-9]{7,8}$/";
        if (preg_match($isPhone, $phone) || preg_match($isTel, $phone)) {
            return true;
        }
        return false;
    }

}